package com.zlc.sykks.service.impl;

import cn.hutool.json.JSONUtil;
import com.zlc.sykks.entity.LoginFormDTO;
import com.zlc.sykks.entity.User;
import com.zlc.sykks.entity.UserDTO;
import com.zlc.sykks.mapper.UserMapper;
import com.zlc.sykks.result.Result;
import com.zlc.sykks.service.UserService;
import com.zlc.sykks.utils.JWTUtils;
import com.zlc.sykks.utils.UserHodler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static com.zlc.sykks.constance.UserConstance.*;

@Service
@Slf4j
public class UserServiceImp implements UserService {
    @Resource
    private UserMapper userMapper;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /*
    * 注册用户
    * */
    @Override
    public Result register(User user) {
        //1.检验数据的合法性
        User user1 = new User();
        user1.setName(user.getName());

        if(userMapper.querySingleUser(user1) != null){
            //说明已经有该用户了
            return Result.error("用户重复");
        }
        if(user.getPassword().length() < 6){
            return Result.error("密码必须超过六位");
        }

        //todo 验证码啊  密码加密 更多的检验 存入threadLocal等等，暑假待开发中

        //2.将数据保存到数据库
        user.setAddress("青青草原");
        user.setAge(18);
        user.setSex("男");
        user.setPhone("15165898888");
        userMapper.addUser(user);

        //3.返回
        return Result.success();
    }

    /*
    * 用户登录
    * */
    @Override
    public Result login(LoginFormDTO loginFormDTO) {
        //todo 后续在优化，先实现
        //1.校验用户
        User u = new User();
        BeanUtils.copyProperties(loginFormDTO,u);
        User user = userMapper.querySingleUser(u);
        //用户不存在or密码错误， 统一返回错误信息
        if(user == null){
            return Result.error("账号或密码错误");
        }

        UserDTO user1 = UserHodler.getUser();
        //2.如果token存在，则不需要生成新token
        if(user1 == null){
            //3.登录成功，生成token
            HashMap<String, String> map = new HashMap<>();
            map.put("id",user.getId().toString());
            String token = JWTUtils.createJWT(map);

            //4.保存到redis
            UserDTO userDTO = new UserDTO();
            BeanUtils.copyProperties(user,userDTO);
            String k = USER_LOGIN_TOKEN_PREFIX + ":" + token;
            stringRedisTemplate.opsForValue().set(k,JSONUtil.toJsonStr(userDTO));
            //设置过期ttl
            stringRedisTemplate.expire(k,USER_LOGIN_TOKEN_TTL, TimeUnit.SECONDS);

            //5.返回 新生成的token
            user1 = new UserDTO();
            BeanUtils.copyProperties(user,user1);
            user1.setToken(token);
            return Result.success(user1);
        }

        //6.返回旧token
        return Result.success(user1);
    }

    /*
    * 用户签到
    * */
    @Override
    public Result sign() {
        //1.获取当前用户
        UserDTO user = UserHodler.getUser();
        //2.获取日期
        LocalDateTime now = LocalDateTime.now();
        int dayOfMonth = now.getDayOfMonth();
        //3.拼接Key
        String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM:"));
        String key = USER_SIGIN_PREFIX + keySuffix + user.getId();
        //4.判断该用户今天是否已签到
        Boolean isSign = stringRedisTemplate.opsForValue().getBit(key, dayOfMonth);
        if(isSign){
            return Result.error(USER_SIGN_ERROR_ALREADYSIGN);
        }
        //5.签到，写入redis
        //写入redis                                     setBit从零开始，所以要-1
        stringRedisTemplate.opsForValue().setBit(key,dayOfMonth-1,true);
        //6.返回结果
        return Result.success();
    }

    /*
     * 获取本月用户连续签到次数
     * */
    @Override
    public Result getSignCount() {
        //1.获取当前年月
        LocalDateTime now = LocalDateTime.now();
        int dayOfMonth = now.getDayOfMonth();
        String date = now.format(DateTimeFormatter.ofPattern(":yyyyMM:"));
        //2.拼接key
        String key = USER_SIGIN_PREFIX + date + UserHodler.getUser().getId();
        //3.获取bitmap   返回一个以List的形式返回一个十进制数字
        List<Long> bitmap = stringRedisTemplate.opsForValue().bitField(
                key,
                BitFieldSubCommands.create()
                        .get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0)
        );
        //4.判断是否有签到记录
        if(bitmap == null || bitmap.size() <= 0){
            return Result.success(0);
        }

        //5. 计算当前签到次数
        Long number = bitmap.get(0);
        //如果值为0，说明没有签到记录
        if(number == 0){
            return Result.success(0);
        }
        int count = 0;
        while(true){
            //进行 与运算，若二进制最后一位为0，则&1=0；若二进制最后一位为1，则&1=1;
            if((number & 1) == 0){
                //签到中断
                break;
            }else{
                //当前天完成签到,++
                count++;
            }
            //右移，判断下一天签到情况
            number >>>= 1;
        }
        //6.返回当前连续签到次数
        log.info("获取当前连续签到次数:{}",count);
        return Result.success(count);
    }

    /*
    *
    * */

}











































